package block

import (
	"WeiXinCode/区块链学习/01_day_hdv2/utils"
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"math/big"
)

// 提供一个效验函数
type ProofOfWork struct {
	block *Block

	// 用于储存哈希值 难度值
	target *big.Int // 系统提供的函数最大的int
}

const Bits = 16 //难度值，用来移动位数
// 获取一个ProofOfWork 用于校验和计算
func NewProofOfWork(block *Block) *ProofOfWork {
	pow := ProofOfWork{
		block: block,
	}
	// 编写难度值 难度值应该是推导出来的，不应该自己编写。
	// 16进制字符串
	//targetStr := "000012cd3bf6641bdb0e2050b76932cbb2e6034a0ddacc1d9bea82a6ba57f7cf"
	//var bigIntTmp big.Int
	//bigIntTmp.SetString(targetStr, 16)

	//	自动生成要挖矿的哈希值
	bigIntTmp := big.NewInt(1)
	bigIntTmp.Lsh(bigIntTmp, 256)  //向左移动256位
	bigIntTmp.Rsh(bigIntTmp, Bits) //向右移动16位
	pow.target = bigIntTmp
	return &pow
}

// 计算/挖矿 得到随机数
func (pow *ProofOfWork) Run() ([]byte, uint64) {
	var nonce uint64
	var hash [32]byte
	for true {
		data2 := pow.preparData(nonce)
		// 做区块hash处理
		hash = sha256.Sum256(data2)
		// 由于hash不能直接比较大小，所以转为big.int比较
		var bigIntTmp big.Int
		bigIntTmp.SetBytes(hash[:]) //计算/挖矿出来值
		//对比 是否小于指定难度值
		if bigIntTmp.Cmp(pow.target) == -1 {
			fmt.Println("挖矿成功！,nonce=", nonce, ", 哈希值为：", hex.EncodeToString(hash[:]))
			break
		}
		nonce++
	}
	return hash[:], nonce
}

// 拼接hash内容，返回拼接内容 传入计算值
func (pow *ProofOfWork) preparData(nonce uint64) []byte {
	block := pow.block
	//拼接数据内容
	tmp := [][]byte{
		utils.UintToByte(block.Version),
		block.PrevBlockHash,
		block.MerKleRoot,
		utils.UintToByte(block.TimeStamp),
		utils.UintToByte(block.Difficulity),
		// TODO block.Data,
		utils.UintToByte(nonce),
	}
	data2 := bytes.Join(tmp, []byte{})
	return data2
}

// 校验是否计算的值正确
func (pow *ProofOfWork) IsValid() bool {
	// 获用户数据
	data := pow.preparData(pow.block.Nonce)
	hash := sha256.Sum256(data)

	var bigIntTmp big.Int
	bigIntTmp.SetBytes(hash[:]) //计算/挖矿出来值

	if bigIntTmp.Cmp(pow.target) == -1 {
		return true
	}
	return false
}
